from Config.myConstant import *
from Config.myConfig import *
from DataAccess.TradedayDataProcess import TradedayDataProcess
from DataPrepare.tickFactorsProcess import tickFactorsProcess
from DataAccess.KLineDataProcess import KLineDataProcess
from DataPrepare.dailyFactorsProcess import dailyFactorsProcess
from Utility.JobLibUtility import JobLibUtility
from typing import List as type_list
from Strategy.baseStrategy.baseStrategy import baseStrategy
import pandas as pd
import numpy as np
import math
import xgboost as xgb
import warnings
from DataAccess.TickDataProcess import TickDataProcess
from DataPrepare.tickFactors.tickDataPrepared import tickDataPrepared
from DataAccess.IndexComponentDataProcess import IndexComponentDataProcess
from Utility.InfluxdbUtility import InfluxdbUtility
from joblib import Parallel, delayed,parallel_backend
from sklearn.metrics import r2_score
########################################################################
class strategy20191202(baseStrategy):
    """利用现有持仓股数来计算收益，不再利用weight来计算"""
    #----------------------------------------------------------------------
    def __init__(self):
        self.name='按照CNN的预测值进行交易'
        pass
    #----------------------------------------------------------------------
    def myParallel(self,codes,startDate,endDate,parameters):
        mydata=Parallel(n_jobs=MYJOBS,verbose=0)(delayed(self.singleCode)(code,startDate,endDate,parameters) for code in codes)
        trade=[]
        statistics=[]
        for data in mydata:
            trade.append(data[0])
            statistics.append(data[1])
        trade=pd.concat(trade)
        statistics=pd.concat(statistics)
        return [trade,statistics]
        pass
    #----------------------------------------------------------------------
    def singleCode(self,code:str,startDate:str,endDate:str,parameters):
        startDate=str(startDate)
        endDate=str(endDate)
        days=list(TradedayDataProcess().getTradedays(startDate,endDate))
        featureColumns=parameters['featureColumns']
        targetColumns=parameters['targetColumns']
        totalCash=parameters['totalCash']
        myStocks=parameters['myStocks']
        predictDatabase=parameters['predictDatabase']
        transactionRatio=parameters['transactionRatio']
        maxExposureRatio=parameters['maxExposureRatio']
        maxVolumeEachTimeRatio=parameters['maxVolumeEachTimeRatio']
        parameter1=parameters['parameter1']
        parameter2=parameters['parameter2']
        dataPath=parameters['dataPath']
        useH5=parameters['h5']
        useRedis=parameters['redis']
        useInfluxdb=parameters['influxdb']
        tick=TickDataProcess()
        tickFactor=tickDataPrepared()
        
        newFeatureColumns=['predictMax','predictMin']
        redisNeedColumns=['B1', 'B10', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'BV1',
       'BV10', 'BV2', 'BV3', 'BV4', 'BV5', 'BV6', 'BV7', 'BV8', 'BV9', 'S1',
       'S10', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', 'S8', 'S9', 'SV1', 'SV10',
       'SV2', 'SV3', 'SV4', 'SV5', 'SV6', 'SV7', 'SV8', 'SV9', 'amount',
       'amountIncrease', 'code', 'dailyOpen', 'dailyPreClose', 'date',
       'highLimit', 'lastPrice',  'lowLimit', 'midPrice',
       'realData', 'tick', 'volume', 'volumeIncrease']+targetColumns
       
        statistics=[]
        trade=[]
        for day in days:
            originalPosition=myStocks[myStocks['code']==code]['hold'].iloc[0]
            if np.isnan(originalPosition)==True:
                continue
            #从本地文件夹读写数据,如果有数据就存到redis里面去
            path=os.path.join(dataPath,day)
            HDF5Utility.pathCreate(path)
            fileName=os.path.join(path,code+'.h5')
            redis_key=code+day
            if ((useRedis==True)& (useH5==True) & (useInfluxdb==True)):
                tickData=super().getDataFromRedis(redis_key)
                if tickData.shape[0]==0:
                    redisHasData=False
                    tickData=super().getDataFromH5(fileName)
                    if tickData.shape[0]==0:
                        H5hasData=False
                        marketData=tick.getTickShotDataFromInfluxdbServer(code,day)
                        predictData=InfluxdbUtility.getDataFromInfluxdb(code,day,predictDatabase)
                        predictCol=list(set(predictData.columns).difference(set(['code','date'])))
                        predictData=predictData[predictCol]
                        if (marketData.shape[0]>0) & (predictData.shape[0]>0):
                            #predictData[newFeatureColumns]=predictData[featureColumns]
                            tickData=pd.merge(marketData,predictData,how='left',left_index=True,right_index=True)
                            influxdbHasData=True
                        else:
                            influxdbHasData=False
                    else:
                        H5hasData=True
                else:
                    redisHasData=True
                if (tickData.shape[0]>0):
                    if redisHasData==False:
                        tickData=tickData[redisNeedColumns]
                        super().saveDataToRedis(tickData,redis_key)
                        pass
                    if H5hasData==False:

                        super().saveDataToH5(tickData,fileName)
                        pass
                pass
            elif (useRedis==True) & (useH5==True) & (useInfluxdb==False):
                tickData=super().getDataFromRedis(redis_key)
                if tickData.shape[0]==0:
                    redisHasData=False
                    tickData=super().getDataFromH5(fileName)
                    if tickData.shape[0]==0:
                        H5hasData=False
                    else:
                        H5hasData=True
                else:
                    redisHasData=True
                if (tickData.shape[0]>0):
                    if redisHasData==False:
                        tickData=tickData[redisNeedColumns]
                        super().saveDataToRedis(tickData,redis_key)
                        pass
                pass
            elif (useRedis==False) & (useH5==True) & (useInfluxdb==False):
                tickData=super().getDataFromH5(fileName)
                pass
            elif (useRedis==False) & (useH5==True) & (useInfluxdb==True):
                redisHasData=False
                tickData=super().getDataFromH5(fileName)
                if tickData.shape[0]==0:
                    H5hasData=False
                    marketData=tick.getTickShotDataFromInfluxdbServer(code,day)
                    predictData=InfluxdbUtility.getDataFromInfluxdb(code,day,predictDatabase)
                    predictCol=list(set(predictData.columns).difference(set(['code','date'])))
                    predictData=predictData[predictCol]
                    if (marketData.shape[0]>0) & (predictData.shape[0]>0):
                        #predictData[newFeatureColumns]=predictData[featureColumns]
                        tickData=pd.merge(marketData,predictData,how='left',left_index=True,right_index=True)
                        influxdbHasData=True
                    else:
                        influxdbHasData=False
                else:
                    H5hasData=True
                if (tickData.shape[0]>0):
                    if H5hasData==False:
                        super().saveDataToH5(tickData,fileName)
                        pass
                pass
            elif (useRedis==False) & (useH5==False) & (useInfluxdb==True):
                marketData=tick.getTickShotDataFromInfluxdbServer(code,day)
                predictData=InfluxdbUtility.getDataFromInfluxdb(code,day,predictDatabase)
                predictCol=list(set(predictData.columns).difference(set(['code','date'])))
                predictData=predictData[predictCol]
                if (marketData.shape[0]>0) & (predictData.shape[0]>0):
                    #predictData[newFeatureColumns]=predictData[featureColumns]
                    tickData=pd.merge(marketData,predictData,how='left',left_index=True,right_index=True)
                    influxdbHasData=True
                else:
                    influxdbHasData=False
                pass
            else:
                tickData=pd.DataFrame()
            
            
            
            
            




            
            if tickData.shape[0]==0:
                continue
            tickData[newFeatureColumns]=tickData[featureColumns]
            preClose=tickData['dailyPreClose'].iloc[0]
            dailyOpen=tickData['dailyOpen'].iloc[0]
            tmpData=tickData[targetColumns+newFeatureColumns].dropna(axis=0, how='any')
            predictMax=np.max(abs(tmpData[newFeatureColumns].values))/100
            r2_1=r2_score(tmpData[targetColumns[0]],tmpData[newFeatureColumns[0]])
            r2_2=r2_score(tmpData[targetColumns[1]],tmpData[newFeatureColumns[1]])
            corr_1=tmpData[[targetColumns[0],newFeatureColumns[0]]].corr().iloc[0,1]
            corr_2=tmpData[[targetColumns[1],newFeatureColumns[1]]].corr().iloc[0,1]
            del(tmpData)
            maxPosition=round(originalPosition,-2)
            maxExposure=round(originalPosition*maxExposureRatio,-2)
            maxVolumeEachTime=max(round(originalPosition*maxVolumeEachTimeRatio,-2),100)
           
            if maxPosition<100:
                continue
            for col in featureColumns:
                if (col in list(tickData.columns))==False:
                    continue
                if tickData[col].isna().sum()==tickData.shape[0]:
                    continue
            for i in parameter1:
                for j in parameter2:
                    parameters={'code':code,'date':day,'maxPosition':maxPosition,'maxExposure':maxExposure,'maxVolumeEachTime':maxVolumeEachTime,'parameter1':i,'parameter2':j,'transactionRatio':transactionRatio}    
                    [trade0,statistics0]=self.strategy(tickData,parameters)
                    if statistics0.shape[0]>0:
                        statistics0['originalPosition']=originalPosition
                        statistics0['dailyOpen']=dailyOpen
                        statistics0['parameter1']=i
                        statistics0['parameter2']=j
                        statistics0['r2_1']=r2_1
                        statistics0['r2_2']=r2_2
                        statistics0['corr_1']=corr_1
                        statistics0['corr_2']=corr_2
                        trade0['parameter1']=i
                        trade0['parameter2']=j
                        statistics.append(statistics0)
                        trade.append(trade0)
            pass
        if len(statistics)==0:
            statistics=pd.DataFrame()
            trade=pd.DataFrame()
        else:
            statistics=pd.concat(statistics)
            
            trade=pd.concat(trade)
        logger.info(f'backtest of {code} complete!!')
        return [trade,statistics]
     #----------------------------------------------------------------------
    def strategy(self,data:pd.DataFrame,parameters):
        
        maxPosition=parameters['maxPosition']
        maxExposure=parameters['maxExposure']
        para1=parameters['parameter1']
        para2=parameters['parameter2']
        transactionRatio=parameters['transactionRatio']
        maxVolumeEachTime=parameters['maxVolumeEachTime']
        code=parameters['code']
        date=parameters['date']
        openPara=para1
        multiplier=para2
        unusedPosition=maxPosition
        todayPosition=0
        myindex={}
        select=list(data.columns)
        for item in data.columns:
            myindex.update({item:select.index(item)})
        mydata=data.values
        openPrice=0
        openAmount=0
        openVolume=0
        maxPrice=0
        minPrice=9999
        stop=False
        longSignal=False
        longCloseSignal=False
        shortSignal=False
        shortCloseSignal=False
        maxDownDraw=0
        openDownDraw=0
        openIndex=-1
        holdTime=0
        transactionIndex=0
        trade=[]
        lastTick=data['tick'].iloc[-1]
        if lastTick>'145500000':
            lastTick='145500000'
        for i in range(len(mydata)):
            tick=mydata[i] 
            maxPredict=tick[myindex['predictMax']]/100.0
            minPredict=tick[myindex['predictMin']]/100.0
            realData=tick[myindex['realData']]
            S1=tick[myindex['S1']]
            B1=tick[myindex['B1']]
            mid=(S1+B1)/2
            SV1=tick[myindex['SV1']]
            BV1=tick[myindex['BV1']]
            time=tick[myindex['tick']]
            date=tick[myindex['date']]
            lastPrice=tick[myindex['lastPrice']]
            if (((lastPrice==0) or (mid==0)) and (time<=lastTick) and (i>=21)):
                #9点31分至14点55分数据出错，报错
                print("error occur!")
                print(mydata[i])
                continue
           
            if ((lastPrice<=0) and (mid<=0)):
                continue
            if mid>0:
                spread=abs(S1-B1)/mid
            elif lastPrice>0:
                spread=0.01/lastPrice
                mid=lastPrice
                S1=lastPrice
                B1=lastPrice
            else:
                spread=0
            #按金额处理maxEachTime及maxExposure
            if lastPrice>0:
                maxVolumeEachTimeByAmount=np.floor((600000/lastPrice)/100.0)*100
                maxExposureByAmount=np.floor((1800000/lastPrice)/100.0)*100
            else:
                maxVolumeEachTimeByAmount=200000
                maxExposureByAmount=600000
            limitMultiplier=1
            if (maxPredict>openPara) &  (multiplier*maxPredict>-(minPredict-2*spread)):
                longSignal=True
                if ((minPredict-2*spread)<0):
                    limitMultiplier=-maxPredict/(minPredict-2*spread)
                    limitMultiplier=min(limitMultiplier,5)
                else:
                    limitMultiplier=5
            else:
                longSignal=False
            if (minPredict<-openPara) & (multiplier*minPredict<-(maxPredict+2*spread)):
                shortSignal=True
                if (maxPredict+2*spread)>0:
                    limitMultiplier=-minPredict/(maxPredict+2*spread)
                    limitMultiplier=min(limitMultiplier,5)
                else:
                    limitMultiplier=5
            else:
                shortSignal=False
            if (todayPosition>0) &  (minPredict<-openPara):
                longCloseSignal=True
            else:
                longCloseSignal=False
            if (todayPosition<0)  &  (maxPredict>openPara):
                shortCloseSignal=True
            else:
                shortCloseSignal=False
            
            maxVolumeEachTime=min(round(parameters['maxVolumeEachTime'],-2),maxVolumeEachTimeByAmount)
            maxExposure=min(parameters['maxExposure'],maxExposureByAmount)
            if maxPosition>0:
                maxVolumeEachTime=max(maxVolumeEachTime,100)
                maxExposure=max(maxVolumeEachTime,100)
            

            increaseToday=tick[myindex['midPrice']]/tick[myindex['dailyPreClose']]-1
            #遇上涨跌停直接不做
            if (abs(increaseToday)>=0.095) & (todayPosition==0):
                break
            #止盈止损
            if todayPosition>0:
                if mid>maxPrice:
                    maxPrice=mid
                openVwapPrice=openAmount/openVolume
                maxDownDraw=min(0,(mid-maxPrice)/openVwapPrice)
                openDownDraw=min(0,(mid-openPrice)/openPrice)
                openYield=(mid-openPrice)/openPrice
                openVwapDownDraw=min(0,(mid-openVwapPrice)/openVwapPrice)
                if openIndex>0:
                    holdTime=i-openIndex
                if ((openDownDraw<=-0.01) | (holdTime>=600)):
                    stop=True
                else:
                    stop=False
                pass
            elif todayPosition<0:
                if mid<minPrice:
                    minPrice=mid
                openVwapPrice=openAmount/openVolume
                maxDownDraw=min(0,(minPrice-mid)/openVwapPrice)
                openDownDraw=min(0,(openPrice-mid)/openPrice)
                openYield=(mid-openPrice)/openPrice
                openVwapDownDraw=min(0,(openVwapPrice-mid)/openVwapPrice)
                if openIndex>0:
                    holdTime=i-openIndex
                if ((openDownDraw<=-0.01) | (holdTime>=600)):
                    stop=True
                else:
                    stop=False
                pass
            if todayPosition!=0:
                if todayPosition>0:#持有多仓
                    myclose=False
                    if  (time>=lastTick) | (increaseToday<-0.095):
                        [averagePrice,sellPosition]=super().sellByTickShotData(tick,myindex,abs(todayPosition),0.05)
                        transactionVolume=sellPosition
                        todayPosition=todayPosition-transactionVolume
                        transactionPrice=averagePrice
                        transactionTime=time
                        transactionDate=date
                        myclose=True
                        pass
                    elif ((shortSignal==True) &(longSignal==False)) | (longCloseSignal==True) | (stop==True):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*BV1,-2),abs(todayPosition))
                        if transactionVolume>0:
                            todayPosition=todayPosition-transactionVolume
                            transactionPrice=B1
                            transactionTime=time
                            transactionDate=date
                            myclose=True
                        pass
                    if  myclose==True:
                        if todayPosition!=0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'sell','volume':transactionVolume,'price':transactionPrice,'longshort':'long','holdTime':holdTime,'status':'closing','transactionID':transactionIndex}
                        if todayPosition==0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'sell','volume':transactionVolume,'price':transactionPrice,'longshort':'long','holdTime':holdTime,'status':'closed','transactionID':transactionIndex}
                            openPrice=0
                            maxPrice=0
                            minPrice=9999
                            maxDownDraw=0
                            openDownDraw=0
                            openIndex=-1
                            holdTime=0
                            openAmount=0
                            openVolume=0
                            stop=False
                            transactionIndex=transactionIndex+1
                        pass
                        trade.append(dict)
                    pass
                elif todayPosition<0:#持有空仓
                    myclose=False
                    if  (time>=lastTick) | (increaseToday>0.095):
                        [averagePrice,buyPosition]=super().buyByTickShotData(tick,myindex,abs(todayPosition),0.05)
                        transactionVolume=buyPosition
                        todayPosition=todayPosition+transactionVolume
                        transactionPrice=averagePrice
                        transactionTime=time
                        transactionDate=date
                        myclose=True
                        pass
                    elif ((longSignal==True)&(shortSignal==False)) | (shortCloseSignal==True)| (stop==True):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*SV1,-2),abs(todayPosition))
                        if transactionVolume>0:
                            todayPosition=todayPosition+transactionVolume
                            transactionPrice=S1
                            transactionTime=time
                            transactionDate=date
                            myclose=True
                        pass
                    if myclose==True:
                        if todayPosition!=0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'buy','volume':transactionVolume,'price':transactionPrice,'longshort':'short','holdTime':holdTime,'status':'closing','transactionID':transactionIndex}
                        if todayPosition==0:
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'buy','volume':transactionVolume,'price':transactionPrice,'longshort':'short','holdTime':holdTime,'status':'closed','transactionID':transactionIndex}
                            openPrice=0
                            maxPrice=0
                            minPrice=9999
                            maxDownDraw=0
                            openIndex=-1
                            holdTime=0
                            openAmount=0
                            openVolume=0
                            stop=False
                            transactionIndex=transactionIndex+1
                        trade.append(dict)
                    pass
            if (unusedPosition>0) & (realData==1) & (time>'093100000') & (time<'145000000'):#仍然可以开仓
                if (SV1>=100/transactionRatio) & (increaseToday>-0.09):
                    if (longSignal==True)& (shortSignal==False) &(todayPosition>=0):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*SV1,-2),unusedPosition,maxExposure-abs(todayPosition))
                        if transactionVolume>0:
                            unusedPosition=unusedPosition-transactionVolume
                            todayPosition=todayPosition+transactionVolume
                            transactionPrice=S1
                            transactionTime=time
                            transactionDate=date
                            if openIndex<0:
                                openIndex=i
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'buy','volume':transactionVolume,'price':transactionPrice,'longshort':'long','holdTime':holdTime,'status':'opening','transactionID':transactionIndex}
                            trade.append(dict)
                            openAmount=openAmount+abs(transactionVolume)*transactionPrice
                            openVolume=openVolume+abs(transactionVolume)
                            if openPrice==0:
                                openPrice=transactionPrice
                                maxPrice=openPrice
                                maxDownDraw=0
                                openDownDraw=0
                            pass
                if (BV1>=100/transactionRatio)& (increaseToday<0.09):
                    if (shortSignal==True)& (longSignal==False) &(todayPosition<=0):
                        transactionVolume=min(maxVolumeEachTime,round(transactionRatio*BV1,-2),unusedPosition,maxExposure-abs(todayPosition))
                        if transactionVolume>0:
                            unusedPosition=unusedPosition-transactionVolume
                            todayPosition=todayPosition-transactionVolume
                            transactionPrice=B1
                            transactionTime=time
                            transactionDate=date
                            if openIndex<0:
                                openIndex=i
                            dict={'date':transactionDate,'tick':transactionTime,'direction':'sell','volume':transactionVolume,'price':transactionPrice,'longshort':'short','holdTime':holdTime,'status':'opening','transactionID':transactionIndex}
                            trade.append(dict)
                            openAmount=openAmount+abs(transactionVolume)*transactionPrice
                            openVolume=openVolume+abs(transactionVolume)
                            if openPrice==0:
                                openPrice=transactionPrice
                                minPrice=openPrice
                                maxDownDraw=0
                                openDownDraw=0
                            pass
            #print(f"{time} {todayPosition}")   
        trade=pd.DataFrame(data=trade)
        if trade.shape[0]>0:
            trade['code']=code
            trade['fee']=trade['price']*0.0001
            selectBuy=trade['direction']=='buy'
            selectSell=trade['direction']=='sell'
            trade.loc[selectSell,'fee']=(trade['fee']+trade['price']*0.001)[selectSell]
            trade.loc[selectBuy,'cashChange']=((-trade['price']-trade['fee'])*trade['volume'])[selectBuy]
            trade.loc[selectSell,'cashChange']=((trade['price']-trade['fee'])*trade['volume'])[selectSell]
            trade['amount']=trade['price']*trade['volume']
            transaction=pd.DataFrame()
            transaction['profit']=trade.groupby('transactionID')['cashChange'].sum()
            transaction['volume']=trade.groupby('transactionID')['volume'].sum()/2
            transaction['amount']=trade.groupby('transactionID')['amount'].sum()/2
            transaction['yield']=transaction['profit']/transaction['amount']
            transaction['holdTime']=round(trade.groupby('transactionID')['holdTime'].max()*0.05,3)
            turnoverRate=round(transaction['volume'].sum()/maxPosition,3)
            winRate=transaction[transaction['yield']>0].shape[0]/transaction.shape[0]
            statistics=[{'count':transaction.shape[0],'profit':transaction['profit'].sum(),'yield':transaction['profit'].sum()/transaction['amount'].sum(),'amount':transaction['amount'].sum(),'turnoverRate':turnoverRate,'winRate':winRate,'totalHold':transaction['holdTime'].sum(),'hold':transaction['holdTime'].sum()/transaction.shape[0]}]
            statistics=pd.DataFrame(data=statistics)
            statistics['code']=code
            statistics['date']=date
            statistics['yieldToAll']=statistics['yield']*statistics['turnoverRate']

            buyVolume=trade[trade['direction']=='buy']['volume'].sum()
            sellVolume=trade[trade['direction']=='sell']['volume'].sum()
            if ((buyVolume-sellVolume)!=0):
                print(f"error!!! Volume unbalanced!!! {date} of {code} with {para1},{para2} wrong!")

        else:
            statistics=pd.DataFrame()
        return [trade,statistics]
########################################################################
